#ifndef PSD2_H_
#define PSD2_H_

#include "Field.h"

enum DuplexSpongeModeType
{
    Absorbing,
    Squeezing
};

class DuplexSpongeMode
{
public:
    __device__ static DuplexSpongeMode Absorbing(int next_absorb_index);
    __device__ static DuplexSpongeMode Squeezing(int index);

    __device__ DuplexSpongeMode();
    
    DuplexSpongeModeType type;
    int next_absorb_index;
    int next_squeeze_index;
};

class Exectuor;
class Psd2
{
public:
    __device__ Psd2();

    __device__ void setExectuor(Exectuor *e);
    __device__ CuVector<Field> hash_many(const CuVector<Field> &input, uint16_t num_outputs);

protected:
    __device__ void absorb(CuVector<Field>& state, DuplexSpongeMode &mode, const CuVector<Field>& input);

    /// Squeeze the specified number of state elements into the output.
    __device__ CuVector<Field> squeeze(CuVector<Field>& state, DuplexSpongeMode &mode, uint16_t num_outputs);

    /// Squeeze the state elements into the output.
    __device__ void squeeze_internal(CuVector<Field>& state, DuplexSpongeMode &mode, CuVector<Field>& output);
    __device__ void apply_ark(CuVector<Field>& state, int round);
    __device__ void apply_s_box(CuVector<Field>& state, bool is_full_round);
    __device__ void apply_mds(CuVector<Field>& state);
    __device__ void permute(CuVector<Field>& state);

    int RATE;
    int CAPACITY;
    int partial_rounds;
    int full_rounds;
    Field alpha;

    Exectuor* m_exectuor;
};
extern __device__ Field *psd2_domain;
extern __device__ CuVector<CuVector<Field>> *psd2_mds;
extern __device__ CuVector<CuVector<Field>> *psd2_ark;

#endif // ! PSD2_H_

